﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;
using System.Linq;
using System.Reflection;

namespace AutoRegister
{
    public static class AutoRegisterServiceCollectionExtensions
    {
        public static IServiceCollection AddAutoRegister(this IServiceCollection services, IConfiguration configuration)
        {
            return services.AddAutoRegister(configuration, null);
        }

        public static IServiceCollection AddAutoRegister(this IServiceCollection services, IConfiguration configuration, Action<AutoRegisterOptions> configure)
        {
            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }

            var options = new AutoRegisterOptions
            {
                AddEntry = true
            };

            configure?.Invoke(options);

            if (options.AssemblyProvider == null)
            {
                options.AssemblyProvider = new DefaultAssemblyProvider(options);
            }

            services.TryAddSingleton<IAssemblyProvider>(options.AssemblyProvider);

            var baseType = typeof(IServiceRegister);

            var registerInstances = options.AssemblyProvider.GetAssemblies().SelectMany(assembly =>
            {
                return assembly.GetTypes().Where(t =>
                {
                    if (baseType == t)
                    {
                        return false;
                    }

                    TypeInfo tInfo = t.GetTypeInfo();

                    if (tInfo.IsAbstract || !tInfo.IsClass || !tInfo.IsPublic)
                    {
                        return false;
                    }

                    return baseType.IsAssignableFrom(t);
                });
            }).Select(type => (IServiceRegister)Activator.CreateInstance(type))
            .ToList();

            foreach (var register in registerInstances)
            {
                register.Register(services, configuration);
            }

            return services;
        }
    }
}
